//
// Created by zhangzeyong on 2024/6/18.
//

#include "stock_engine.h"
#include "opt_trade_handler.h"

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <map>
#include <unordered_set>
#include <chrono>
#include <iomanip>
#include <sys/inotify.h>

std::chrono::system_clock::time_point parseTimestamp(const std::string& timestampStr)
{
    std::tm tm = {};
    if (!strptime(timestampStr.c_str(), "%Y-%m-%d %H:%M:%S", &tm))
    {
        throw std::runtime_error("Failed to parse timestamp.");
    }
    auto msPos = timestampStr.find_last_of('.');
    if (msPos != std::string::npos && msPos + 4 <= timestampStr.size())
    {
        int ms = std::stoi(timestampStr.substr(msPos + 1, 3));
        return std::chrono::system_clock::from_time_t(std::mktime(&tm)) + std::chrono::milliseconds(ms);
    }
    else
    {
        return std::chrono::system_clock::from_time_t(std::mktime(&tm));
    }
}


bool parseLine(const std::string& line, const std::chrono::system_clock::time_point& startTime,
               std::string& connection_id, std::string& title, std::map<std::string, std::string>& keyValueMap)
{
    // Find the positions of the brackets
    size_t firstBracketPos = line.find('[');
    size_t secondBracketPos = line.find(']', firstBracketPos + 1);
    size_t thirdBracketPos = line.find('[', secondBracketPos + 1);
    size_t fourthBracketPos = line.find(']', thirdBracketPos + 1);
    size_t fifthBracketPos = line.find('[', fourthBracketPos + 1);
    size_t sixthBracketPos = line.find(']', fifthBracketPos + 1);

    if (firstBracketPos == std::string::npos || secondBracketPos == std::string::npos ||
        thirdBracketPos == std::string::npos || fourthBracketPos == std::string::npos ||
        fifthBracketPos == std::string::npos || sixthBracketPos == std::string::npos ||
        secondBracketPos <= firstBracketPos || fourthBracketPos <= thirdBracketPos ||
        sixthBracketPos <= fifthBracketPos)
    {
        std::cerr << "Invalid line format: " << line << "\n";
        return false;
    }

    // Extract the timestamp between the first pair of brackets
    connection_id = line.substr(firstBracketPos + 1, secondBracketPos - firstBracketPos - 1);

    // Extract the title between the second pair of brackets
    title = line.substr(thirdBracketPos + 1, fourthBracketPos - thirdBracketPos - 1);

    // Extract the substring within the third pair of brackets
    std::string kvString = line.substr(fifthBracketPos + 1, sixthBracketPos - fifthBracketPos - 1);

    // Parse key-value pairs
    std::istringstream kvStream(kvString);
    std::string pair;
    while (std::getline(kvStream, pair, ','))
    {
        size_t colonPos = pair.find(':');
        if (colonPos != std::string::npos)
        {
            std::string key = pair.substr(0, colonPos);
            std::string value = pair.substr(colonPos + 1);
            keyValueMap[key] = value;
        }
    }
    return true;
}

void stock_engine::start()
{
    auto exit_loop = false;

    // ����
    new std::thread([&]
    {
        auto* pclTradeApi = new CCliOptTradeApi();
        // ������Ӧ������ʵ��
        COptTradeHandler clTradeHandler(pclTradeApi);
        // ע����Ӧʵ��
        pclTradeApi->RegisterSpi(&clTradeHandler);
        int request_id = 1;
        while (true)
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
            if (exit_loop)
            {
                break;
            }
            QueueMsg msg[ITEM_SIZE_1024];

            // ��ȡ����
            auto size = m_trade_queue.try_dequeue_bulk(msg, ITEM_SIZE_1024);
            for (auto i = 0; i < size; ++i)
            {
                clTradeHandler.connection_id = msg[i].m_connection_id;
                switch (msg[i].m_type)
                {
                case Init:
                    {
                        if (clTradeHandler.bConnected)
                        {
                            LOG4CPLUS_INFO(
                                TRADE_LOGGER, clTradeHandler.ConnectionHead() + "[OnConnected][msg:success]");
                            break;
                        }

                        // ���÷�������ַ/�˿�
                        LOG4CPLUS_INFO(TRADE_LOGGER, "server_ip:" << msg[i].m_keyValueMap["server_ip"]);
                        LOG4CPLUS_INFO(TRADE_LOGGER, "server_port:" << msg[i].m_keyValueMap["server_port"]);

                        pclTradeApi->RegisterServer(msg[i].m_keyValueMap["server_ip"].c_str(),
                                                    std::stoi(msg[i].m_keyValueMap["server_port"]));
                        // ��ʼ������
                        auto ret = pclTradeApi->Init();
                        if (ret != 0)
                        {
                            LOG4CPLUS_INFO(TRADE_LOGGER, "init fail:" << ret);
                            exit(EXIT_SUCCESS);
                        }
                    }
                    break;
                case ReqUserLogin:
                    {
                        if (clTradeHandler.bLogged)
                        {
                            LOG4CPLUS_INFO(
                                TRADE_LOGGER, clTradeHandler.ConnectionHead() + "[OnRspUserLogin]" << "[msg:success]");
                            break;
                        }
                        auto chChannel = *msg[i].m_keyValueMap["chChannel"].c_str(); //��������
                        auto chOpSite = msg[i].m_keyValueMap["chOpSite"];
                        auto iRetCode = pclTradeApi->
                            RegisterAcct(chChannel, chOpSite.c_str(), strlen(chOpSite.c_str()));

                        if (iRetCode)
                        {
                            LOG4CPLUS_INFO(TRADE_LOGGER,
                                           clTradeHandler.ConnectionHead() + "[RegisterAcctError][msg:" + GBKToUTF8(pclTradeApi->
                                           GetLastErrorText()) +
                                           "]");
                        }

                        CReqOptUserLoginField stReqUserLoginField = {0};
                        strcpy(stReqUserLoginField.szAcctType, msg[i].m_keyValueMap["szAcctType"].c_str());
                        strcpy(stReqUserLoginField.szAcctId, msg[i].m_keyValueMap["szAcctId"].c_str());
                        stReqUserLoginField.chUseScope = *msg[i].m_keyValueMap["chUseScope"].c_str();
                        stReqUserLoginField.chAuthType = *msg[i].m_keyValueMap["chAuthType"].c_str();
                        stReqUserLoginField.chEncryptType = *msg[i].m_keyValueMap["chEncryptType"].c_str();

                        strcpy(stReqUserLoginField.szAuthData, msg[i].m_keyValueMap["szAuthData"].c_str());
                        strcpy(stReqUserLoginField.szEncryptKey, msg[i].m_keyValueMap["szEncryptKey"].c_str());

                        iRetCode = pclTradeApi->ReqUserLogin(&stReqUserLoginField, request_id++);
                        if (iRetCode)
                        {
                            LOG4CPLUS_INFO(TRADE_LOGGER,
                                           clTradeHandler.ConnectionHead() + "[ReqUserLoginError][msg:" + GBKToUTF8(pclTradeApi->
                                           GetLastErrorText()) +
                                           "]");
                        }
                    }
                    break;
                case ReqQryExpendableFund:
                    {
                        for (const auto& pair : clTradeHandler.m_account_info)
                        {
                            CReqOptExpendableFundField stField = {0};
                            char buffer[32] = {0};
                            stField.llCustCode = pair.second.llCustCode;
                            stField.llCuacctCode = pair.second.llCuacctCode;

                            stField.iValueFlag = 15;
                            int iRetCode = pclTradeApi->ReqQryExpendableFund(&stField, request_id++);
                            if (iRetCode)
                            {
                                LOG4CPLUS_INFO(TRADE_LOGGER,
                                               clTradeHandler.ConnectionHead() + "[ReqQryExpendableFund][msg:" +
                                               GBKToUTF8(pclTradeApi->GetLastErrorText()) +
                                               "]");
                            }
                        }
                    }
                    break;
                case ReqQryExpendableCu:
                    {
                        for (const auto& pair : clTradeHandler.m_account_info)
                        {
                            CReqOptExpendableCuField stField = {0};
                            stField.llCustCode = pair.second.llCustCode;
                            stField.llCuacctCode = pair.second.llCuacctCode;

                            strcpy(stField.szStkbd, pair.second.szStkbd);
                            strcpy(stField.szTrdacct, pair.second.szTrdacct);
                            strcpy(stField.szStkpbu, pair.second.szStkpbu);

                            int iRetCode = pclTradeApi->ReqQryExpendableCu(&stField, request_id++);
                            if (iRetCode)
                            {
                                LOG4CPLUS_INFO(TRADE_LOGGER,
                                               clTradeHandler.ConnectionHead() + "[ReqQryExpendableCuError][msg:" +
                                               GBKToUTF8(pclTradeApi->GetLastErrorText()) +
                                               "]");
                            }
                        }
                    }
                    break;
                case ReqQryCurrDayOrder:
                    {
                        for (const auto& pair : clTradeHandler.m_account_info)
                        {
                            CReqOptCurrDayOrderField stField = {0};
                            stField.llCustCode = pair.second.llCustCode;
                            stField.llCuacctCode = pair.second.llCuacctCode;

                            strcpy(stField.szStkbd, pair.second.szStkbd);
                            strcpy(stField.szTrdacct, pair.second.szTrdacct);
                            //                            stField.chQueryFlag = '0';                // ��ѯ���� 0:���ȡ���� 1:��ǰȡ���� ����ȫ������
                            //                            stField.iQryNum = 200;                // ��ѯ����

                            int iRetCode = pclTradeApi->ReqQryCurrDayOrder(&stField, request_id++);
                            if (iRetCode)
                            {
                                LOG4CPLUS_INFO(TRADE_LOGGER,
                                               clTradeHandler.ConnectionHead() + "[ReqQryCurrDayOrderError][msg1:" +
                                               GBKToUTF8(pclTradeApi->GetLastErrorText()) +
                                               "]");
                            }
                        }
                    }
                    break;
                case ReqOrder:
                    {
                        CReqOptOrderField stField = {0};
                        auto key = msg[i].m_keyValueMap["chStkex"];
                        stField.llCustCode = clTradeHandler.m_account_info[key[0]].llCustCode;
                        stField.llCuacctCode = clTradeHandler.m_account_info[key[0]].llCuacctCode;
                        strcpy(stField.szStkbd, clTradeHandler.m_account_info[key[0]].szStkbd);
                        strcpy(stField.szTrdacct, clTradeHandler.m_account_info[key[0]].szTrdacct);
                        strcpy(stField.szStkpbu, clTradeHandler.m_account_info[key[0]].szStkpbu);
                        stField.iIntOrg = clTradeHandler.m_account_info[key[0]].iIntOrg;

                        stField.iStkBiz = std::stoi(msg[i].m_keyValueMap["iStkBiz"]); // ֤ȯҵ��(��������)
                        stField.iStkBizAction = std::stoi(msg[i].m_keyValueMap["iStkBizAction"]);
                        stField.llOrderQty = std::stoi(msg[i].m_keyValueMap["llOrderQty"]);
                        strcpy(stField.szOptNum, msg[i].m_keyValueMap["szOptNum"].c_str());
                        strcpy(stField.szOrderPrice, msg[i].m_keyValueMap["szOrderPrice"].c_str());
                        strcpy(stField.szOrderIdEx, msg[i].m_keyValueMap["szOrderIdEx"].c_str());

                        stField.chEncryptType = *msg[i].m_keyValueMap["chEncryptType"].c_str();
                        strcpy(stField.szEncryptKey, msg[i].m_keyValueMap["szEncryptKey"].c_str());

                        int iRetCode = pclTradeApi->ReqOrder(&stField, request_id++);
                        if (iRetCode)
                        {
                            LOG4CPLUS_INFO(TRADE_LOGGER,
                                           clTradeHandler.ConnectionHead() + "[ReqOrderError][msg:" + GBKToUTF8(pclTradeApi->GetLastErrorText()) +
                                           "]");
                        }
                    }
                    break;
                case ReqCancelOrder:
                    {
                        CReqOptCancelOrderField stReqOrderKillField = {0};
                        auto key = msg[i].m_keyValueMap["chStkex"];

                        stReqOrderKillField.llCuacctCode = clTradeHandler.m_account_info[key[0]].llCuacctCode;
                        stReqOrderKillField.iIntOrg = clTradeHandler.m_account_info[key[0]].iIntOrg;
                        strcpy(stReqOrderKillField.szStkbd, clTradeHandler.m_account_info[key[0]].szStkbd);

                        strcpy(stReqOrderKillField.szOrderId, msg[i].m_keyValueMap["szOrderId"].c_str());
                        stReqOrderKillField.iOrderBsn = std::stoi(msg[i].m_keyValueMap["iOrderBsn"]); // ί������

                        int iRetCode = pclTradeApi->ReqCancelOrder(&stReqOrderKillField, request_id++);
                        if (iRetCode)
                        {
                            LOG4CPLUS_INFO(TRADE_LOGGER,
                                           clTradeHandler.ConnectionHead() + "[ReqCancelOrderError][msg:" + GBKToUTF8(pclTradeApi
                                           ->GetLastErrorText()) +
                                           "]");
                        }
                    }
                    break;
                }
            }
        }
    });

    std::string filename = "../config/input.txt";
    int last_pos = 1;
    auto startTime = std::chrono::system_clock::now();
    std::unordered_set<std::string> processed_lines;

    std::ofstream clear_file(filename, std::ios::trunc);
    if (!clear_file.is_open())
    {
        LOG4CPLUS_INFO(TRADE_LOGGER, "�޷����ļ�");
    }
    clear_file.close();

    while (true)
    {
        std::ifstream file(filename);

        if (!file.is_open())
        {
            LOG4CPLUS_INFO(TRADE_LOGGER, "�޷����ļ�");
            return;
        }

        std::string line;
        int pos = 0;
        while (std::getline(file, line))
        {
            pos += 1;
            if (pos < last_pos)
            {
                continue;
            }
            last_pos += 1;
            
            // 先用完整的line（包含前6位编码）去重
            if (processed_lines.find(line) != processed_lines.end())
            {
                continue;
            }
            processed_lines.insert(line);
            
            // 去重后，去掉前6位编码
            if (line.length() >= 6)
            {
                line = line.substr(6);
            }
            
            std::string title;
            std::string connection_id;
            std::map<std::string, std::string> keyValueMap;
            parseLine(line, startTime, connection_id, title, keyValueMap);

            LOG4CPLUS_INFO(TRADE_LOGGER, line);
            if (title == "Init")
            {
                m_trade_queue.enqueue(QueueMsg(connection_id, Init, keyValueMap));
            }
            else if (title == "ReqUserLogin")
            {
                m_trade_queue.enqueue(QueueMsg(connection_id, ReqUserLogin, keyValueMap));
            }
            else if (title == "ReqQryExpendableFund")
            {
                m_trade_queue.enqueue(QueueMsg(connection_id, ReqQryExpendableFund, keyValueMap));
            }
            else if (title == "ReqQryExpendableCu")
            {
                m_trade_queue.enqueue(QueueMsg(connection_id, ReqQryExpendableCu, keyValueMap));
            }
            else if (title == "ReqQryCurrDayOrder")
            {
                m_trade_queue.enqueue(QueueMsg(connection_id, ReqQryCurrDayOrder, keyValueMap));
            }
            else if (title == "ReqOrder")
            {
                m_trade_queue.enqueue(QueueMsg(connection_id, ReqOrder, keyValueMap));
            }
            else if (title == "ReqCancelOrder")
            {
                m_trade_queue.enqueue(QueueMsg(connection_id, ReqCancelOrder, keyValueMap));
            }
        }

        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        file.close();
    }
}
